<?php
class FileTransfer
{
	private $_prefixPath = null;
	private $_destinationPath = null;
	private $_targetPath = null;	
	private $_allowedFileExts = null;
	
	public function __construct($destinationPath, $prefixPath = null)
	{
		/*check destination path*/
		$this->_destinationPath = $destinationPath;
		if(!is_dir($this->_destinationPath) || !is_writable($this->_destinationPath)){
			throw new Exception('FileTransfer Exception: Destination directory "'.$this->_destinationPath.'" does not exist or is not writable!');	
		}
		
		/*validate prefix path*/
		if($prefixPath !== null){
			$this->_prefixPath = preg_replace('/[?.*"<>|]+/', '', $prefixPath);	
		}
		$this->_targetPath = $this->_destinationPath.'/'.$this->_prefixPath;
		
		/*build target path*/
		if(!is_dir($this->_targetPath)){
			if(!@mkdir($this->_targetPath, 0, true)){
				throw new Exception('FileTransfer Exception: Error occurred while creating upload dir!');	
			}
		}
	}
	
	/**
     * Set allowed file extension.
     * @param 	(array|string)$strAllowedFileExts, 
	 			(string)$delimiter (optional - used if $strAllowedFileExts is not array)
     * @return void
     */
	public function setAllowedFileExts($strAllowedFileExts, $delimiter = ';'){
		$this->_allowedFileExts = $strAllowedFileExts;
		if(!is_array($this->_allowedFileExts)){
			$this->_allowedFileExts = explode($delimiter, $this->_allowedFileExts);
		}
	}
	
	/**
     * Get target path (included both destinationPath and prefixPath)
     * @param none
     * @return string
     */
	public function getTargetPath(){
		return $this->_targetPath;
	}
	
	/**
     * Upload file to target path
     * @param 	(array) $file (optional - default $_FILES HTTP File Upload variables)
	 			(int)$chmod (optional - default 0644), 
	 			(bool)$rename (optional - default true): FALSE means replace the name of uploaded file (avoid overwriting existed file)
     * @return string
     */
	public function upload($files = null, $chmod = 0644, $rename=true){
		if($files === null){
			$files = $_FILES;
		}

		/*create uploaded queue*/
		$queue = $this->queue($files);
		
		//move uploaded files from queue
		foreach($queue as $file){
			$arrFileInfo = array();
			/*upload article images*/
			if($file["error"] == UPLOAD_ERR_OK) {
				
				/*get uploaded file info*/
				$tempFile = $file["tmp_name"];
				$fileName = $this->sanitize($file['name']);
				$fileSize = $file['size'];
				$fileType = $file['type'];
				$fileExtension = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
	
				/*check file is allowed to upload?*/			
				if(!empty($this->_allowedFileExts) && !in_array($fileExtension, $this->_allowedFileExts)){
					@unlink($tempFile);
					throw new Exception("extension '$fileExtension' is not allowed to upload");
				}
				
				/*Check uploaded file path*/
				$fileNamePrefix = '';
				$filePath = $this->_targetPath.'/'.$fileName;
				if(file_exists($filePath)){
					if($rename == true){
						$fileNamePrefix = time().'_';
						$filePath = $this->_targetPath.'/'.$fileNamePrefix.$fileName;
					} else {
						@unlink($filePath);
					}
				}
				
				/*move file to server*/
				if (@is_uploaded_file($tempFile) && @move_uploaded_file($tempFile, $filePath)){
					/*Set permissions on filename*/
					@chmod($filePath, $chmod);
					/*set file info*/
					$arrFileInfo = array('filename'=>$fileName, 
										 'extension'=>$fileExtension, 
										 'size'=>round($fileSize/1024, 2), /*KB*/
										 'type'=>$fileType, 
										 'path'=>$this->_prefixPath.'/'.$fileNamePrefix.$fileName);
				}
				return $arrFileInfo;
			}
			
			/*Exceptions*/
			if($file["error"] == UPLOAD_ERR_INI_SIZE) throw new Exception('The uploaded file exceeds the upload_max_filesize directive in php.ini');
			if($file["error"] == UPLOAD_ERR_FORM_SIZE) throw new Exception('The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form');
			if($file["error"] == UPLOAD_ERR_PARTIAL) throw new Exception('The uploaded file was only partially uploaded');
			if($file["error"] == UPLOAD_ERR_NO_FILE) throw new Exception('No file was uploaded');
			if($file["error"] == UPLOAD_ERR_NO_TMP_DIR) throw new Exception('Missing a temporary folder');
			if($file["error"] == UPLOAD_ERR_CANT_WRITE) throw new Exception('Failed to write file to disk');
			if($file["error"] == UPLOAD_ERR_EXTENSION) throw new Exception('A PHP extension stopped the file upload');
		}
	}
	
	private function sanitize($fileName, $length=0){
		$fileName = preg_replace('/(-{2,})|(\.{2,})+/', '-', strtolower(trim(preg_replace('/[^\w.]|[|_]+/', '-', $fileName), '-')));
		if(is_int($length) && $length > 0 && mb_strlen($fileName, "UTF-8") > $length) 
		{
			$fileName = mb_substr($fileName, 0, $length, "UTF-8");
		}
		return $fileName;
	}


	public function downloadFromUrl($url, $chmod = 0644, $rename=true){
		$arrFileInfo = array();
		try{
			if (filter_var($url, FILTER_VALIDATE_URL) === false) {
				throw new Exception("{$url} is not a valid URL");
			}
			
			/*get downloaded file info*/
			$parts = explode('/', parse_url($url, PHP_URL_PATH));
			$fileName = $this->sanitize(end($parts));
			$fileSize = '0';
			$fileType = '';
			$fileExtension = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
			
			/*check file is allowed to download?*/			
			if(!empty($this->_allowedFileExts) && !in_array($fileExtension, $this->_allowedFileExts)){
				throw new Exception("extension '$fileExtension' is not allowed to download");
			}
			
			/*Check uploaded file path*/
			$fileNamePrefix = '';
			$filePath = $this->_targetPath.'/'.$fileName;
			if(file_exists($filePath)){
				if($rename == true){
					$fileNamePrefix = time().'_';
					$filePath = $this->_targetPath.'/'.$fileNamePrefix.$fileName;
				} else {
					@unlink($filePath);
				}
			}
			
			/*move file to server*/
			if(@copy($url, $filePath)){
				/*Set permissions on filename*/
					@chmod($filePath, $chmod);
					/*set file info*/
					$arrFileInfo = array('filename'=>$fileName, 
										 'extension'=>$fileExtension, 
										 'size'=>round($fileSize/1024, 2), /*KB*/
										 'type'=>$fileType, 
										 'path'=>$this->_prefixPath.'/'.$fileNamePrefix.$fileName);
			}
			return $arrFileInfo;
			
		} catch(Exception $ex){
			throw new Exception($ex->getMessage());	
		}
	}

	private function queue($files){
		if(empty($files)) return nulll;
		$queue = array();
		if(isset($files["error"]) && $files["error"] == UPLOAD_ERR_OK){
			$queue[] = $files;
		}
		else{
			foreach($files as $section=>$file){ 
				//single upload
				if(isset($file["error"]) && !is_array($file["error"])){
					if($files[$section]["error"] == UPLOAD_ERR_NO_FILE){
						continue;
					}
					//add to queue
					$queue[] = $files[$section];
				}
				//multi upload
				elseif(isset($file["error"]) && is_array($file["error"])){
					$new = array();
					foreach( $file as $key => $item ){					
						foreach($item as $i => $value){
							if(is_array($file[$key][$i])){
								throw new Exception('FileTransfer Exception: too complex structure;');
							}
							$new[$i][$key] = $value;   
						}
					}
					//add to queue
					foreach($new as $item){
						$queue[] = $item;
					}
				}
				else{
					/* LỖI FILE SIZE IS TOO LARGE */
					throw new Exception('FileTransfer Exception: Invalid parameter');	
				}
			}
		}
		return $queue;
	}

	public static function rearrangeGlobalFilesArray(array $files = null) {
		$new = array();
		$files = !empty($files)?$files:$_FILES;
		foreach( $files as $name => $file ){
			//check if uploading multi files?
			if(isset($file['error']) && is_array($file['error'])){
				foreach( $file as $i => $item ){
					foreach($item as $key => $value)
						$new[$name][$key][$i] = $value;    
				}
			}
			else{
				$new[$name][] = $file;	
			}
		}
		return $new;
	}

}